# coding=utf8
__author__ = 'lijian'
import threading
import uplink
import rtu.manager
import datetime
import logging
import Queue
from protocol.uplink.packet import *
import protocol.uplink.frameFactory
import config
import setting
from xml.dom import minidom
from collections import deque
import os


class EventDeque():
    def __init__(self):

        self.items = deque()
        self.writeCursor = 0
        self.readCursor = 0

    def append(self, item):
        if len(self.items) < 256:
            self.items.append(item)
            self.writeCursor += 1

            if self.writeCursor == 255:
                self.writeCursor = 0
        else:
            self.items[self.writeCursor] = item

            if self.writeCursor < 255:
                self.writeCursor += 1
            else:
                self.writeCursor = 0

    def get(self, index):
        return self.items[index]

    def save(self, dom, element):
        element.setAttribute("writeCursor", "%d" % self.writeCursor)
        element.setAttribute("readCursor", "%d" % self.readCursor)
        for index in range(len(self.items)):
            event = self.items[index]
            ele = dom.createElement("event")
            ele.setAttribute("aid", "%d" % index)
            ele.setIdAttribute("aid")
            ele.setAttribute("code", "%d" % event.code)
            ele.setAttribute("time", "%s" % event.time.strftime("%Y-%m-%d %H:%M:%S"))
            ele.setAttribute("data", "%r" % event.data)
            # ele.setAttribute("upload", "%r" % event.upload)

            element.appendChild(ele)

    def load(self, element):
        self.writeCursor = int(element.getAttribute("writeCursor"))
        self.readCursor = int(element.getAttribute("readCursor"))

        eventElements = element.getElementsByTagName("event")
        for ele in eventElements:
            id = int(ele.getAttribute("aid"))
            code = int(ele.getAttribute("code"))
            text = ele.getAttribute("data")
            data = eval(text)
            time = datetime.datetime.strptime(ele.getAttribute("time"), "%Y-%m-%d %H:%M:%S")
            # upload = True if ele.getAttribute("upload").encode("ISO-8859-1").lower() == "true" else False

            item = Event(code=code, data=data, time=time)
            self.items.append(item)

    def flush(self, readCursor):
        self.readCursor = readCursor


    def newEventsCount(self):
        start = self.readCursor
        end = self.writeCursor
        count = 0
        if start < end:
            count = end - start
        else:
            count = 255 + end - start

        return count

    def readEvent(self):
        events = []
        cursor = self.readCursor
        while cursor != self.writeCursor:
            item = self.items[cursor]
            events.append(item)
            cursor += 1
            if cursor >= 255:
                cursor = 0

        start = self.readCursor
        end = self.writeCursor
        return start, end, events


con = threading.Condition()
mutex = threading.Lock()

importanceEvents = EventDeque()
normalEvents = EventDeque()

initiativeEvents = Queue.Queue()

_newEvent = False

_upload = False

_mockGen = False


class Event(object):
    def __init__(self, code, data=None, time=None):
        self.code = code
        self.data = []

        if data:
            self.data.extend(data)

        if time:
            self.time = time
        else:
            self.time = datetime.datetime.now()

        self.upload = False
        self.isNew = False

    def len(self):
        return len(self.data) + 5

    def encode(self):
        self.raw = []
        self.raw.append(self.code)
        self.raw.append(self.len())
        self.raw.extend(byteUtil.encodeA15(self.time))
        self.raw.extend(self.data)

        return self.raw


def checkLampException(term):
    global _newEvent, _upload, _mockGen
    mutex.acquire()

    # term.checkException()
    #
    # if _newEvent:
    # _upload = True
    # _newEvent = False

    # if term.uid[5] == 1:
    #     if term.alarmStatue == 1 and not _mockGen:
    #         genMockEvent(term)
    #         _upload = True
    #         _newEvent = False
    #         _mockGen = True
    #     elif term.alarmStatue == 0:
    #         _mockGen = False

    mutex.release()


_powerOnException = {"status": False, "count": 0, "time": 0}
_powerOffException = {"status": False, "count": 0, "time": 0}


def genMockEvent(holder):
    # data = []
    # data.append(1)  # 事件开始
    # erc = Event(code=1, data=data)
    # addEvent(erc)

    # data = []
    # erc = Event(code=2, data=data)
    # addEvent(erc)

    # data = []
    # data.append(1)  # 事件开始
    # erc = Event(code=3, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # erc = Event(code=4, data=data)
    # addEvent(erc)

    # data = []
    # data.append(1)  # 事件开始
    # erc = Event(code=5, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.append(1)
    # erc = Event(code=6, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.append(1)
    # erc = Event(code=7, data=data)
    # addEvent(erc)

    point=0
    analog = rtu.manager.getAnalog(point)
    pointConfig = rtu.manager.getAnalogConfig(point)

    # data = []
    # data.append(1)  # 事件开始
    # data.append(point)  # 模拟量点号
    # data.append(pointConfig["type"])  # 模拟量类型
    # data.extend(rtu.manager.endoceAnalogPointValue(point))
    # erc = Event(code=8, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.append(point)  # 模拟量点号
    # data.append(pointConfig["type"])  # 模拟量类型
    # data.extend(rtu.manager.endoceAnalogPointValue(point))
    # erc = Event(code=9, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.append(point)  # 模拟量点号
    # data.append(pointConfig["type"])  # 模拟量类型
    # data.extend(rtu.manager.endoceAnalogPointValue(point))
    # erc = Event(code=10, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # erc = Event(code=11, data=data)
    # addEvent(erc)

    # data = []
    # data.append(1)  # 事件开始
    # erc = Event(code=12, data=data)
    # addEvent(erc)
    #
    # data = [1,1,1]
    # erc = Event(code=13, data=data)
    # addEvent(erc)
    #
    # data = [0,0,0]
    # erc = Event(code=14, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.extend(byteUtil.encodeShort(5))
    # data.extend(byteUtil.encodeShort(1))
    # data.append(1)
    # erc = Event(code=15, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.extend(byteUtil.encodeShort(5))
    # data.extend(byteUtil.encodeShort(1))
    # data.append(2)
    # erc = Event(code=16, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)
    # data.extend(byteUtil.encodeShort(holder.holderNo))
    # data.append(1)
    # erc = Event(code=17, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)
    # data.extend(byteUtil.encodeShort(holder.holderNo))
    # data.append(1)
    # erc = Event(code=18, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.extend(byteUtil.encodeShort(holder.holderNo))  # 灯号
    # data.extend(byteUtil.encodeA2(holder.a))
    # erc = Event(code=19, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.extend(byteUtil.encodeShort(holder.holderNo))  # 灯号
    # data.extend(byteUtil.encodeA2(holder.a))
    # erc = Event(code=20, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.extend(byteUtil.encodeShort(holder.holderNo))  # 灯号
    # data.extend(byteUtil.encodeA2(holder.a))
    # data.extend(byteUtil.encodeA5(holder.pf))
    # erc = Event(code=21, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.extend(byteUtil.encodeShort(holder.holderNo))  # 灯号
    # data.extend(byteUtil.encodeA2(holder.v))
    # data.extend(byteUtil.encodeA2(holder.a))
    # erc = Event(code=22, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.append(1)  # 事件开始
    # data.extend(byteUtil.encodeShort(holder.holderNo))  # 灯号
    # data.extend(byteUtil.encodeA2(holder.v))
    # data.extend(byteUtil.encodeA2(holder.a))
    # erc = Event(code=23, data=data)
    # addEvent(erc)
    #
    # data = []
    # data.extend(byteUtil.encodeShort(2))
    # data.extend(byteUtil.encodeA15(datetime.datetime.now()))
    # erc = Event(code=24, data=data)
    # addEvent(erc)
    #
    data = []
    data.append(1)
    data.append(1)
    data.append(0)
    data.append(0)
    erc = Event(code=25, data=data)
    addEvent(erc)


def checkRtuException():
    global _newEvent, _upload
    mutex.acquire()

    # checkPowerOnException()
    # checkPowerOffException()
    # checkAnalogException()
    #
    # if _newEvent:
    # _upload = True
    # _newEvent = False

    mutex.release()


# 正常开灯失败检查
def checkPowerOnException():
    if rtu.manager.isPowerOn():
        values = []
        values.append(rtu.manager.getSignalValue(8))
        values.append(rtu.manager.getSignalValue(9))
        values.append(rtu.manager.getSignalValue(10))
        values.append(rtu.manager.getSignalValue(11))

        exception = False
        for value in values:
            if value == 0:
                exception = True
                break

        if exception:
            _powerOnException["count"] += 1
        else:
            _powerOnException["status"] = False
            _powerOnException["count"] = 0

        if _powerOnException["status"] == False and _powerOnException["count"] > 5:
            _powerOnException["status"] = True
            erc = Event(code=3)
            addEvent(erc)


# 正常关灯失败检查
def checkPowerOffException():
    if not rtu.manager.isPowerOn():
        values = []
        values.append(rtu.manager.getSignalValue(8))
        values.append(rtu.manager.getSignalValue(9))
        values.append(rtu.manager.getSignalValue(10))
        values.append(rtu.manager.getSignalValue(11))

        exception = False
        for value in values:
            if value == 1:
                exception = True
                break

        if exception:
            _powerOffException["count"] += 1
        else:
            _powerOffException["status"] = False
            _powerOffException["count"] = 0

        if not _powerOffException["status"] and _powerOffException["count"] > 5:
            _powerOffException["status"] = True
            erc = Event(code=5)
            addEvent(erc)


# 模拟量上下限检测
def checkAnalogException():
    for point in range(81):
        analog = rtu.manager.getAnalog(point)
        pointConfig = rtu.manager.getAnalogConfig(point)

        if pointConfig and pointConfig["enable"] == 1:

            if rtu.manager.isPowerOn():
                # 模拟量越上限(开灯时间内)记录
                if analog["value"] > pointConfig["onUpperLimit"]:
                    analog["alarm"]["powerOnUpperLimit"]["count"] += 1

                    if not analog["alarm"]["powerOnUpperLimit"]["status"] and \
                                    analog["alarm"]["powerOnUpperLimit"]["count"] > 1:
                        analog["alarm"]["powerOnUpperLimit"]["status"] = True

                        data = []
                        data.append(1)  # 事件开始
                        data.append(point)  # 模拟量点号
                        data.append(pointConfig["type"])  # 模拟量类型
                        data.extend(rtu.manager.endoceAnalogPointValue(point))
                        erc = Event(code=9, data=data)
                        addEvent(erc)

                else:
                    if analog["alarm"]["powerOnUpperLimit"]["status"]:
                        data = []
                        data.append(0)  # 事件结束
                        data.append(point)  # 模拟量点号
                        data.append(pointConfig["type"])  # 模拟量类型
                        data.extend(rtu.manager.endoceAnalogPointValue(point))
                        erc = Event(code=9, data=data)
                        addEvent(erc)

                    analog["alarm"]["powerOnUpperLimit"]["status"] = False
                    analog["alarm"]["powerOnUpperLimit"]["count"] = 0

                # 模拟量越下限(开灯时间内)记录
                if analog["value"] < pointConfig["onLowerLimit"]:
                    analog["alarm"]["powerOnLowerLimit"]["count"] += 1

                    if not analog["alarm"]["powerOnLowerLimit"]["status"] \
                            and analog["alarm"]["powerOnLowerLimit"]["count"] > 1:
                        analog["alarm"]["powerOnLowerLimit"]["status"] = True

                        data = []
                        data.append(1)  # 事件开始
                        data.append(point)  # 模拟量点号
                        data.append(pointConfig["type"])  # 模拟量类型
                        data.extend(rtu.manager.endoceAnalogPointValue(point))
                        erc = Event(code=10, data=data)
                        addEvent(erc)

                else:
                    if analog["alarm"]["powerOnLowerLimit"]["status"]:
                        data = []
                        data.append(0)  # 事件结束
                        data.append(point)  # 模拟量点号
                        data.append(pointConfig["type"])  # 模拟量类型
                        data.extend(rtu.manager.endoceAnalogPointValue(point))
                        erc = Event(code=10, data=data)
                        addEvent(erc)

                    analog["alarm"]["powerOnLowerLimit"]["status"] = False
                    analog["alarm"]["powerOnLowerLimit"]["count"] = 0
            else:
                # 模拟量越上限(关灯时间内)记录
                if analog["value"] < pointConfig["offLowerLimit"]:
                    analog["alarm"]["powerOffLowerLimit"]["count"] += 1

                    if not analog["alarm"]["powerOffLowerLimit"]["status"] \
                            and analog["alarm"]["powerOffLowerLimit"]["count"] > 1:
                        analog["alarm"]["powerOffLowerLimit"]["status"] = True

                        data = []
                        data.append(1)  # 事件开始
                        data.append(point)  # 模拟量点号
                        data.append(pointConfig["type"])  # 模拟量类型
                        data.extend(rtu.manager.endoceAnalogPointValue(point))
                        erc = Event(code=8, data=data)
                        addEvent(erc)

                else:
                    if analog["alarm"]["powerOffLowerLimit"]["status"]:
                        data = []
                        data.append(0)  # 事件结束
                        data.append(point)  # 模拟量点号
                        data.append(pointConfig["type"])  # 模拟量类型
                        data.extend(rtu.manager.endoceAnalogPointValue(point))
                        erc = Event(code=8, data=data)
                        addEvent(erc)

                    analog["alarm"]["powerOffLowerLimit"]["status"] = False
                    analog["alarm"]["powerOffLowerLimit"]["count"] = 0


# 是否是重要事件
def isImportanceEvent(erc):
    ec = config.eventConfig[erc.code]
    return ec["importance"]


def isInitiative(erc):
    ec = config.eventConfig[erc.code]
    return ec["initiative"]


def loadEvents():
    if not os.path.exists(setting.EVENT_RECORD):
        logging.warn("%s not exist" % setting.EVENT_RECORD)
        return

    try:
        doc = minidom.parse(setting.EVENT_RECORD)
        root = doc.documentElement

        importanceElement = root.getElementsByTagName("importance")[0]
        importanceEvents.load(importanceElement)

    except Exception as e:
        logging.error("parse %s error. %r" % (setting.EVENT_RECORD, e.message))


def saveEvents():
    impl = minidom.getDOMImplementation()
    dom = impl.createDocument(None, 'events', None)
    root = dom.documentElement

    importanceElement = dom.createElement("importance")
    root.appendChild(importanceElement)
    importanceEvents.save(dom, importanceElement)

    f = open(setting.EVENT_RECORD, 'w')
    dom.writexml(f, addindent='  ', newl='\n', encoding='utf-8')
    f.close()


def addEvent(event):
    global _newEvent
    _newEvent = True
    logging.info("gen alarm,code: %d" % event.code)

    if isImportanceEvent(event):
        importanceEvents.append(event)
    else:
        normalEvents.append(event)

        # if isInitiative(event):
        # initiativeEvents.put(event)


def alarmMonitor():
    global _upload

    while True:
        if _upload:
            start, end, events = importanceEvents.readEvent()

            data = []
            data.append(importanceEvents.newEventsCount())
            data.append(0)
            # data.append(normalEvents.newEventsCount())
            data.append(start)
            data.append(end)

            for event in events:
                data.extend(event.encode())

            du = DataUnit(pn=0, fn=0x1, data=data)
            frame = protocol.uplink.frameFactory.buildInitiativeFrame(0xe, du)

            if uplink.syncSend(frame):
                importanceEvents.flush(end)

            saveEvents()
            _upload = False

        time.sleep(1)


def startMonitor():
    th = threading.Thread(target=alarmMonitor, args=())
    th.setDaemon(True)
    th.start()